package org.aner.tester.utils;

import org.aner.tester.constant.Constant;
import org.aner.tester.utils.text.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * reflect utility class
 * 
 * @author aner
 */
@SuppressWarnings("rawtypes")
public class ReflectUtils
{
    /**
     * merge the set method of field
     *
     * @param fieldName
     * @return String
     */
    public static String getSetMethodName(String fieldName) {
        if (StringUtils.isEmpty(fieldName)) {
            return null;
        }
        int startIndex = 0;
        if (fieldName.charAt(0) == Constant.UNDERLINE) {
            startIndex = 1;
        }
        return Constant.SET
                + fieldName.substring(startIndex, startIndex + 1).toUpperCase()
                + fieldName.substring(startIndex + 1);
    }

    /**
     * check set method
     *
     * @param methods
     * @param fieldSetMet
     * @return boolean
     */
    public static boolean checkSetMethod(Method[] methods, String fieldSetMet) {
        if (StringUtils.isEmpty(fieldSetMet)) {
            return false;
        }
        for (Method method : methods) {
            if (fieldSetMet.equals(method.getName())) {
                return true;
            }
        }
        return false;
    }
    /**
     * set the value of field
     * @param bean
     * @param method
     * @param value
     * @throws Exception
     */
    public static void setField(Object bean, Method method, Object value) throws Exception{
        method.invoke(bean, value);
    }

    /**
     * get all fields of a class，contained the fields of supper class
     *
     * @param clazz
     * @return
     */
    public static Field[] getFields(Class<?> clazz) {
        Set<Field> fieldSet = new HashSet<>();
        Field[] dFields = clazz.getDeclaredFields();
        if (null != dFields && dFields.length > 0) {
            fieldSet.addAll(Arrays.asList(dFields));
        }

        Class<?> superClass = clazz.getSuperclass();
        if (superClass != Object.class) {
            Field[] superFields = getFields(superClass);
            if (null != superFields && superFields.length > 0) {
                for(Field field:superFields){
                    // use the field of subclass, of which the field name is the same to a supper class
                    if(!fieldSet.contains(field)){
                        fieldSet.add(field);
                    }
                }
            }
        }
        Field[] result=new Field[fieldSet.size()];
        fieldSet.toArray(result);
        return result;
    }

    /**
     * get all methods of a class，contained the methods of supper class
     *
     * @param clazz
     * @return
     */
    public static Method[] getMethods(Class<?> clazz) {
        Set<Method> methodSet = new HashSet<>();
        Method[] currentMethods = clazz.getDeclaredMethods();
        if (null != currentMethods && currentMethods.length > 0) {
            methodSet.addAll(Arrays.asList(currentMethods));
        }

        Class<?> superClass = clazz.getSuperclass();
        if (superClass != Object.class) {
            Method[] supperMethods = getMethods(superClass);
            if (null != supperMethods && supperMethods.length > 0) {
                for(Method method: supperMethods){
                    // use the method of subclass, of which the method name is the same to a supper class
                    if(!methodSet.contains(method)){
                        methodSet.add(method);
                    }
                }
            }
        }
        Method[] result=new Method[methodSet.size()];
        methodSet.toArray(result);
        return result;
    }
}
